package com.ff.xyh.consumer.chatroom.listener;

import com.ff.xyh.common.entity.message.RoomBanned;
import com.ff.xyh.common.entity.message.RoomMessage;
import com.ff.xyh.common.entity.post.PostRoom;
import com.ff.xyh.common.utils.CommonUtil;
import com.ff.xyh.common.vo.message.ActionTypeAndRoomMessage;
import com.ff.xyh.common.vo.message.RoomMessageVo;
import com.ff.xyh.common.vo.user.UserBaseInfo;
import com.ff.xyh.consumer.chatroom.config.RoomRabbitConfig;
import com.ff.xyh.consumer.chatroom.config.Rooms;
import com.ff.xyh.consumer.chatroom.config.UserLock;
import com.ff.xyh.consumer.chatroom.config.Users;
import com.ff.xyh.consumer.chatroom.controller.RoomUserController;
import com.ff.xyh.feign.client.message.RoomBannedClient;
import com.ff.xyh.feign.client.message.RoomMessageClient;
import com.ff.xyh.feign.client.post.PostRoomClient;
import com.ff.xyh.feign.client.userfront.FrontUserClient;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.websocket.Session;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @Auther FF
 * @Date 2022年 11月 30日
 * @Time 00:38
 */
@Component
public class ChatRoomRabbitListener {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private FrontUserClient frontUserClient;

    @Autowired
    private RoomMessageClient roomMessageClient;

    @Autowired
    private RoomBannedClient roomBannedClient;

    @Autowired
    private PostRoomClient postRoomClient;

    @RabbitListener(queues = RoomRabbitConfig.QUEUE_SERVER_ROOM_1)
    public void roomQueue1Consumer1(ActionTypeAndRoomMessage message) {

        System.out.println("roomQueue1Consumer1 \n " +
                "queue: server.room.q1 \n " +
                "routingKey: RoomServer1 \n" +
                "controller with: { " + message + " }, when " + LocalTime.now());
//        获取消息体中的数据
        int actionType = message.getActionType();
        RoomMessageVo roomMessageVo = CommonUtil.getMessageInMap(message.getMessageBody(), RoomMessageVo.class);
        String userId = roomMessageVo.getUserId();
        String roomId = roomMessageVo.getRoomId();
        String userRoutingKey = (String) redisTemplate.opsForValue().get(userId);
        UserBaseInfo user = CommonUtil.getDataInResultObject(frontUserClient.getUserBaseInfoById(userId), UserBaseInfo.class);
        CopyOnWriteArraySet<String> roomUsers = Rooms.getRooms().get(roomId);
        if (roomUsers == null) { // 聊天室不存在 可能是第一个进入房间的用户 尝试创建
            Map<String, CopyOnWriteArraySet<String>> rooms = Rooms.getRooms();
            synchronized (rooms) {
                if (!rooms.containsKey(roomId)) {
                    // roomId 和 当前服务器的 房间路由 绑定到redis 成功后才在服务器创建房间
                    boolean hasRoomKey = redisTemplate.opsForValue().setIfAbsent(roomId, RoomRabbitConfig.ROUTING_KEY_ROOM_1);
                    boolean registeredRoomInServer =  redisTemplate.opsForValue().get(roomId).equals(RoomRabbitConfig.ROUTING_KEY_ROOM_1);
                    if(hasRoomKey || registeredRoomInServer) {
                        roomUsers = new CopyOnWriteArraySet<>();
                        rooms.put(roomId, roomUsers);
                    }
                }
            }
            if (roomUsers == null || ! roomUsers.add(userId)) { // 创建失败 或 用户进入失败
                ActionTypeAndRoomMessage messageToUser =
                        ActionTypeAndRoomMessage.type(7)
                                .data("message", new RoomMessageVo()
                                        .setToUserId(userId)
                                        .setRoomId(roomId)
                                        .setMessage("房间连接失败，请退出重试")
                                );
                String roomUserRoutingKey = (String) redisTemplate.opsForValue().get(userId);
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        roomUserRoutingKey,
                        messageToUser);
                return ;
            } else if (roomUsers != null && actionType == 5){ // 创建成功并且用户是想进入房间
                ActionTypeAndRoomMessage messageToUser =
                        ActionTypeAndRoomMessage.type(6)
                                .data("message", new RoomMessageVo()
                                        .setToUserId(userId)
                                        .setRoomId(roomId)
                                        .setUserNumber(roomUsers.size())
                                        .setMessage("用户: " + user.getNickname() + " 进入房间")
                                );
                String roomUserRoutingKey = (String) redisTemplate.opsForValue().get(userId);
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        roomUserRoutingKey,
                        messageToUser);
                return ;
            }
        }
        if (actionType == 5) {          // 进入群聊

            ActionTypeAndRoomMessage messageToUser =
                    ActionTypeAndRoomMessage.type(actionType);

            PostRoom postRoom = CommonUtil.getDataInResultObject(postRoomClient.getPostRoomById(roomId), PostRoom.class);
            if (postRoom == null) {
                messageToUser.setActionType(7)
                        .data("message", new RoomMessageVo()
                                .setToUserId(userId)
                                .setMessage("群聊并不存在，无法进入")
                        );
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        userRoutingKey,
                        messageToUser);
                return ;
            }

            RoomBanned roomBanned = CommonUtil.getDataInResultObject(
                    roomBannedClient.getRoomBannedByRoomIdAndBanner(roomId, userId),
                    RoomBanned.class);
            if (roomBanned != null) {
                messageToUser.setActionType(15)
                        .data("message", new RoomMessageVo()
                                .setToUserId(userId)
                                .setMessage("您已被禁止进入该群聊！"));
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        userRoutingKey,
                        messageToUser);
                return ;
            }

            if (roomUsers.add(userId)) {
                RoomMessageVo roomMessageVoToUser = new RoomMessageVo();
                roomMessageVoToUser
                        .setRoomId(roomId)
                        .setUserNumber(roomUsers.size())
                        .setMessage("用户: " + user.getNickname() + " 进入房间");
                messageToUser.data("message", roomMessageVoToUser);
                for (String roomUserId : roomUsers) {
                    roomMessageVoToUser.setToUserId(roomUserId);
                    String roomUserRoutingKey = (String) redisTemplate.opsForValue().get(roomUserId);
                    rabbitTemplate.convertAndSend(
                            RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                            roomUserRoutingKey,
                            messageToUser);
                }
            } else {
                messageToUser.setActionType(7)
                        .data("message", new RoomMessageVo()
                                .setToUserId(userId)
                                .setMessage("进入失败"));
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        userRoutingKey,
                        messageToUser);
            }

        } else if (actionType == 8) {   // 退出群聊
            RoomBanned roomBanned = CommonUtil.getDataInResultObject(
                    roomBannedClient.getRoomBannedByRoomIdAndBanner(roomId, userId),
                    RoomBanned.class);
            if (roomBanned != null) { // 如果已被踢出 则不能发消息 直接返回
                return ;
            }
            roomUsers.remove(userId);
            if (roomUsers.isEmpty()) {
                Rooms.getRooms().remove(roomId);
                redisTemplate.delete(roomId);
                return ;
            }
            RoomMessageVo roomMessageVoToUser = new RoomMessageVo();
            roomMessageVoToUser
                    .setRoomId(roomId)
                    .setGmtCreated(new Date())
                    .setUserNumber(roomUsers.size())
                    .setMessage( "用户: " + user.getNickname() + " 离开房间");
            ActionTypeAndRoomMessage messageToUser = ActionTypeAndRoomMessage.type(actionType);
            messageToUser.data("message", roomMessageVoToUser);
            for (String roomUserId : roomUsers) {
                roomMessageVoToUser.setToUserId(roomUserId);
                String roomUserRoutingKey = (String) redisTemplate.opsForValue().get(roomUserId);
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        roomUserRoutingKey,
                        messageToUser);
            }
        } else if (actionType == 9) { // 群聊消息发送
            ActionTypeAndRoomMessage messageToUser = ActionTypeAndRoomMessage.type(actionType);
            RoomMessage roomMessage = new RoomMessage();
            roomMessage
                    .setRoomId(roomMessageVo.getRoomId())
                    .setUserId(roomMessageVo.getUserId())
                    .setMessage(roomMessageVo.getMessage());
            // 应该先将消息发送到数据库然后得到消息的id 再发送给用户 如果数据库添加消息失败 则反馈给前端
            RoomMessage savedRoomMessage = CommonUtil.getDataInResultObject(roomMessageClient.addMessage(roomMessage), RoomMessage.class);
            if (savedRoomMessage == null) { // 保存失败
                messageToUser.setActionType(11)
                        .data("message", new RoomMessageVo()
                                .setToUserId(userId)
                                .setMessage("消息发送失败")
                        );
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        userRoutingKey,
                        messageToUser);
                return ;
            }
            // 保存成功 开始反馈给所有人
            roomMessageVo
                    .setId(savedRoomMessage.getId())
                    .setUserNickname(user.getNickname())
                    .setUserAvatar(user.getAvatar())
                    .setGmtCreated(savedRoomMessage.getGmtCreated());
            messageToUser.data("message", roomMessageVo);
            for (String roomUserId : roomUsers) {
                roomMessageVo.setToUserId(roomUserId);
                String roomUserRoutingKey = (String) redisTemplate.opsForValue().get(roomUserId);
                if (roomUserId.equals(userId)) {
                    rabbitTemplate.convertAndSend(
                            RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                            roomUserRoutingKey,
                            messageToUser.setActionType(10));
                } else {
                    rabbitTemplate.convertAndSend(
                            RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                            roomUserRoutingKey,
                            messageToUser.setActionType(9));
                }
            }


        } else if (actionType == 12) {   // 踢用户出群
            ActionTypeAndRoomMessage messageToUser = ActionTypeAndRoomMessage.type(actionType);

            String bannerId = roomMessageVo.getToUserId();
            PostRoom postRoom = CommonUtil.getDataInResultObject(postRoomClient.getPostRoomById(roomId), PostRoom.class);
            UserBaseInfo operator = CommonUtil.getDataInResultObject(frontUserClient.getUserBaseInfoById(userId), UserBaseInfo.class);
            UserBaseInfo banner = CommonUtil.getDataInResultObject(frontUserClient.getUserBaseInfoById(bannerId), UserBaseInfo.class);

            if (postRoom == null || operator == null || banner == null || bannerId.equals(userId)) {
                messageToUser.setActionType(14)
                        .data("message", new RoomMessageVo()
                                .setToUserId(userId)
                                .setMessage("参数错误，踢出失败")
                        );
                // 给踢人者
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        userRoutingKey,
                        messageToUser);
                return ;
            }

            RoomBanned roomBanned =
                    CommonUtil.getDataInResultObject(roomBannedClient.addRoomBanned(
                        new RoomBanned()
                                .setRoomId(roomId)
                                .setOperatorId(userId)
                                .setBannerId(roomMessageVo.getToUserId()
                                )), RoomBanned.class);

            if (roomBanned == null) { // 踢出不生效
                // 给踢人者
                messageToUser.setActionType(14)
                        .data("message", new RoomMessageVo()
                                .setToUserId(userId)
                                .setMessage("数据保存失败，无法永久性踢出")
                        );
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        userRoutingKey,
                        messageToUser);
                // 给被踢者
                String bannerRoutingKey = (String) redisTemplate.opsForValue().get(bannerId);
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        bannerRoutingKey,
                        ActionTypeAndRoomMessage.type(15)
                                .data("message", new RoomMessageVo()
                                        .setToUserId(bannerId)
                                        .setRoomId(roomId)
                                        .setMessage("您已被踢出房间"))
                );
                roomUsers.remove(bannerId);
                return ;
            }
            RoomMessageVo roomMessageVoToUser = new RoomMessageVo();
            roomMessageVoToUser
                    .setRoomId(roomId)
                    .setUserNumber(roomUsers.size()-1)
                    .setGmtCreated(new Date())
                    .setMessage( "用户: " + banner.getNickname() + " 被管理员: " + operator.getNickname() + " 踢出房间");

            messageToUser.data("message", roomMessageVoToUser);

            for (String roomUserId : roomUsers) {
                roomMessageVoToUser.setToUserId(roomUserId);
                String roomUserRoutingKey = (String) redisTemplate.opsForValue().get(roomUserId);
                if (roomUserId.equals(userId)) { // 操作人
                    rabbitTemplate.convertAndSend(
                            RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                            roomUserRoutingKey,
                            messageToUser.setActionType(13));
                    continue ;
                } else if (roomUserId.equals(bannerId)) { // 被踢者
                    rabbitTemplate.convertAndSend(
                            RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                            roomUserRoutingKey,
                            messageToUser.setActionType(15));
                    continue ;
                }
                rabbitTemplate.convertAndSend(
                        RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                        roomUserRoutingKey,
                        messageToUser.setActionType(12));
            }
            roomUsers.remove(bannerId);
        } else {
            ActionTypeAndRoomMessage messageToUser =
                    ActionTypeAndRoomMessage.type(0)
                            .data("message", new RoomMessageVo()
                                    .setToUserId(userId)
                                    .setMessage("未知消息类型，连接将断开")
                            );
            rabbitTemplate.convertAndSend(
                    RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                    userRoutingKey,
                    messageToUser);
        }
    }

    // 用户消息队列
    @RabbitListener(queues = RoomRabbitConfig.QUEUE_SERVER_USER_1)
    public void userQueue1Consumer1(ActionTypeAndRoomMessage actionTypeAndRoomMessage) {
        System.out.println("userQueue1Consumer1 got message: " + actionTypeAndRoomMessage);
        RoomMessageVo roomMessageVo = CommonUtil.getMessageInMap(actionTypeAndRoomMessage.getMessageBody(), RoomMessageVo.class);
        String userId = roomMessageVo.getToUserId();
        int actionType = actionTypeAndRoomMessage.getActionType();
        RoomUserController roomUserController = Users.getUsers().get(userId);
        if (roomUserController != null) {
            Session session = roomUserController.getSession();
//            session.getAsyncRemote().sendObject(messageOwnerAndRoomMessage); // 发送封装的对象 生产环境
            session.getAsyncRemote().sendText(actionTypeAndRoomMessage.toString()); // 测试消息 开发环境
            try {
                if (actionType == 0 || actionType == 7 || actionType == 15 || actionType == 16) {
                    session.close();
                    if (actionType == 16) {
                        rabbitTemplate.convertAndSend(
                                RoomRabbitConfig.EXCHANGE_DIRECT_CHAT,
                                roomMessageVo.getId(),
                                ActionTypeAndRoomMessage.type(5).data(
                                        "message", new RoomMessageVo().setUserId(userId)
                                ));
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("用户已断开连接");
        }
    }

    // 用户登录锁消息队列
    @RabbitListener(queues = RoomRabbitConfig.QUEUE_SERVER_USER_LOCK_1)
    public void userLockQueue1Consumer1(ActionTypeAndRoomMessage actionTypeAndRoomMessage) {
        System.out.println("userLockQueue1Consumer1 got message: " + actionTypeAndRoomMessage);
        RoomMessageVo roomMessageVo = CommonUtil.getMessageInMap(actionTypeAndRoomMessage.getMessageBody(), RoomMessageVo.class);
        String userId = roomMessageVo.getUserId();
        UserLock userLock = UserLock.getLocks().get(userId);
        if (userLock != null) {
            userLock.confirmed();
        } else {
            System.out.println("RoomUserController.UserLock is null");
        }
        System.out.println("ROUTING_KEY_USER_LOCK_1 has been notified when " + LocalDateTime.now());
    }
}
